package xxl.robotlegs.framework.impl.lifecycle 
{
	import xxl.robotlegs.framework.api.IContextLifecycleSignal;
	import xxl.robotlegs.framework.api.ILifecycle;
	
	/**
	 * ...
	 * @author XXL
	 */
	public class Lifecycle implements ILifecycle
	{
		private var _state:String = LifecycleState.UNINITIALIZED;
		private var _target:Object;
		
		private var _initialize:LifecycleTransition;
		private var _suspend:LifecycleTransition;
		private var _resume:LifecycleTransition;
		private var _destroy:LifecycleTransition;
		private var _signal:LifecycleSignal;
		
		public function Lifecycle(target:Object, contextLifecycleSignal:IContextLifecycleSignal = null) 
		{
			_target = target;
			if (contextLifecycleSignal && contextLifecycleSignal is LifecycleSignal)
			{
				_signal = contextLifecycleSignal as LifecycleSignal;
			}
			else
			{
				_signal = new LifecycleSignal(this);
			}
			configureTransitions();
		}
		
		public function get signal():LifecycleSignal
		{
			return _signal;
		}
		
		//----------private
		
		private function configureTransitions():void
		{
			_initialize = new LifecycleTransition(LifecycleSignal.PRE_INITIALIZE, this)
				.fromStates(LifecycleState.UNINITIALIZED)
				.toStates(LifecycleState.INITIALIZING, LifecycleState.ACTIVE)
				.withEvents(LifecycleSignal.PRE_INITIALIZE, LifecycleSignal.INITIALIZE, LifecycleSignal.POST_INITIALIZE);
				
			_suspend = new LifecycleTransition(LifecycleSignal.PRE_SUSPEND, this)
				.fromStates(LifecycleState.ACTIVE)
				.toStates(LifecycleState.SUSPENDING, LifecycleState.SUSPENDED)
				.withEvents(LifecycleSignal.PRE_SUSPEND, LifecycleSignal.SUSPEND, LifecycleSignal.POST_SUSPEND);
				
			_resume = new LifecycleTransition(LifecycleSignal.PRE_RESUME, this)
				.fromStates(LifecycleState.SUSPENDED)
				.toStates(LifecycleState.RESUMING, LifecycleState.ACTIVE)
				.withEvents(LifecycleSignal.PRE_RESUME, LifecycleSignal.RESUME, LifecycleSignal.POST_RESUME);
			
			_destroy = new LifecycleTransition(LifecycleSignal.PRE_DESTROY, this)
				.fromStates(LifecycleState.SUSPENDED, LifecycleState.ACTIVE)
				.toStates(LifecycleState.DESTROYING, LifecycleState.DESTROYED)
				.withEvents(LifecycleSignal.PRE_DESTROY, LifecycleSignal.DESTROY, LifecycleSignal.POST_DESTROY);
		}
		
		private function reportError(message:String):void
		{
			const error:LifecycleError = new LifecycleError(message);
			if (_signal.getNumListenersByKey(LifecycleSignal.ERROR) > 0)
			{
				_signal.dispatchFun(LifecycleSignal.ERROR, error);
			}
			else
			{
				throw error;
			}
		}
		
		//----------internal
		
		internal function setCurrentState(state:String):void
		{
			if (_state == state)
			{
				return;
			}
			_state = state;
			_signal.dispatchFun(LifecycleSignal.STATE_CHANGE);
		}
		
		/* INTERFACE xxl.robotlegs.core.ILifecycle */
		
		public function get state():String 
		{
			return _state;
		}
		
		public function get target():Object 
		{
			return _target;
		}
		
		public function get uninitialized():Boolean 
		{
			return _state == LifecycleState.UNINITIALIZED;
		}
		
		public function get initialized():Boolean 
		{
			return _state != LifecycleState.UNINITIALIZED
				&& _state != LifecycleState.INITIALIZING;
		}
		
		public function get active():Boolean 
		{
			return _state == LifecycleState.ACTIVE;
		}
		
		public function get suspended():Boolean 
		{
			return _state == LifecycleState.SUSPENDED;
		}
		
		public function get destroyed():Boolean 
		{
			return _state == LifecycleState.DESTROYED;
		}
		
		public function initialize(callback:Function = null):void 
		{
			_initialize.enter(callback);
		}
		
		public function suspend(callback:Function = null):void 
		{
			_suspend.enter(callback);
		}
		
		public function resume(callback:Function = null):void 
		{
			_resume.enter(callback);
		}
		
		public function destroy(callback:Function = null):void 
		{
			_destroy.enter(callback);
		}
		
		public function beforeInitializing(handler:Function):ILifecycle 
		{
			uninitialized || reportError(LifecycleError.LATE_HANDLER_ERROR_MESSAGE);
			_initialize.addBeforeHandler(handler);
			return this;
		}
		
		public function whenInitializing(handler:Function):ILifecycle 
		{
			initialized && reportError(LifecycleError.LATE_HANDLER_ERROR_MESSAGE);
			_signal.addToMap(LifecycleSignal.INITIALIZE, handler);
			return this;
		}
		
		public function afterInitializing(handler:Function):ILifecycle 
		{
			initialized && reportError(LifecycleError.LATE_HANDLER_ERROR_MESSAGE);
			_signal.addToMap(LifecycleSignal.POST_INITIALIZE, handler);
			return this;
		}
		
		public function beforeSuspending(handler:Function):ILifecycle 
		{
			_suspend.addBeforeHandler(handler);
			return this;
		}
		
		public function whenSuspending(handler:Function):ILifecycle 
		{
			_signal.addToMap(LifecycleSignal.SUSPEND, handler);
			return this;
		}
		
		public function afterSuspending(handler:Function):ILifecycle 
		{
			_signal.addToMap(LifecycleSignal.POST_SUSPEND, handler);
			return this;
		}
		
		public function beforeResuming(handler:Function):ILifecycle 
		{
			_resume.addBeforeHandler(handler);
			return this;
		}
		
		public function whenResuming(handler:Function):ILifecycle 
		{
			_signal.addToMap(LifecycleSignal.RESUME, handler);
			return this;
		}
		
		public function afterResuming(handler:Function):ILifecycle 
		{
			_signal.addToMap(LifecycleSignal.POST_RESUME, handler);
			return this;
		}
		
		public function beforeDestroying(handler:Function):ILifecycle 
		{
			_destroy.addBeforeHandler(handler);
			return this;
		}
		
		public function whenDestroying(handler:Function):ILifecycle 
		{
			_signal.addToMap(LifecycleSignal.DESTROY, handler);
			return this;
		}
		
		public function afterDestroying(handler:Function):ILifecycle 
		{
			_signal.addToMap(LifecycleSignal.POST_DESTROY, handler);
			return this;
		}
		
	}

}